﻿using System;
using System.Web;
using System.Web.UI;
using System.IO;
using System.Web.UI.HtmlControls;
using System.Text.RegularExpressions;

namespace SystemX.Web
{
	/// <summary>
	/// A helper class for renderring a view
	/// </summary>
	/// <typeparam name="T">view Control type.Commonly 'Mecox.Core.Web.View.BaseView'</typeparam>
	/// <remarks>
	/// 负责将BLL层的数据传递给视图(View),同时获得视图的内容
	/// http://weblogs.asp.net/stefansedich/archive/2008/03/10/a-generic-viewmanager-helper-class.aspx
	/// </remarks>
	public class ViewManager<T> where T : Control
	{

        #region Properties

        private T _control = default(T);

        /// <summary>
        /// Gives you access to the control you are rendering allows
        /// you to set custom properties etc.
        /// </summary>
        public T Control
        {
            get
            {
                return _control;
            }
        }
		/// <summary>
		/// html form of the control.Todo:Make it public if need...
		/// </summary>
		protected HtmlForm Form {
			get
			{
				return _formOfControl;
			}
		}
		private bool _extractFormTags = true;//TODO:set via constructor if needed
        // Used as a placeholder page to render the control on.
        private Page _holder;
		private HtmlForm _formOfControl;
        /// <summary>
        /// whether the view depends on a existing page instance
        /// </summary>
        private bool _hasDependingPage;
		private bool _verifyRenderingInForm;

        #endregion

        #region Constructor
        /// <summary>
        /// Rendering a view which has no ascx file...
        /// </summary>
        /// <param name="viewCtr">view control</param>
        /// <param name="verifyRenderingInForm">
        /// whether verifyRenderingInForm.
        /// If your control contains gridview,set this parameter to true.
        /// </param>
        public ViewManager(T viewCtr, bool verifyRenderingInForm):this(viewCtr,null,verifyRenderingInForm)
        {
        }
        /// <summary>
        /// Rendering a view which has no ascx file...
        /// </summary>
        /// <param name="viewCtr">view control</param>
        /// <param name="verifyRenderingInForm">
        /// whether verifyRenderingInForm.
        /// If your control contains gridview,set this parameter to true.
        /// </param>
        public ViewManager(T viewCtr,Page dependingPage, bool verifyRenderingInForm)
        {
            initHolderPage(dependingPage);
            _control = viewCtr;
            _verifyRenderingInForm = verifyRenderingInForm;
        }

        /// <summary>
        /// Default constructor for this view manager, pass in the path for the control
        /// that this view manager is render.
        /// </summary>
        /// <param name="inPath"></param>
        public ViewManager(string inPath):this(inPath,null)
        {
        }
        /// <summary>
        /// Renderring a view which depends on a page instance.
		/// If u construct a viewmanager with this constructor,
		/// the view to be render won't raise any of its event handlers,such as "Page_Load","On_PreInit"...
        /// </summary>
        /// <param name="dependingPage">view's depending page.If your control need access to Page.Request,you should specify the page.</param>
        /// <param name="viewPath">view's virtual path</param>
        public ViewManager(string viewPath, Page dependingPage):this(viewPath,dependingPage,false)
        {
        }
		/// <summary>
		/// dynamically loading user control
		/// </summary>
		/// <param name="viewPath">view's virtual path</param>
		/// <param name="dependingPage">depending page</param>
		/// <param name="verifyRenderingInForm">
		/// whether verifyRenderingInForm.
		/// If your control contains gridview,set this parameter to true.
		/// </param>
		public ViewManager(string viewPath, Page dependingPage, bool verifyRenderingInForm) {
			_verifyRenderingInForm = verifyRenderingInForm;
			//Init the holder page
            initHolderPage(dependingPage);

			// Create an instance of our control
			_control = (T)_holder.LoadControl(viewPath);
		}
        #endregion

        #region Rendering

        /// <summary>
        /// Renders the current control.
        /// </summary>
        /// <returns></returns>
        public string Render()
        {
            // Add it to our holder page.
            addControlToHolderPage(_verifyRenderingInForm);
            //renderring job
            try
            {
                using (StringWriter sw = new StringWriter())
                {
                    if (!_hasDependingPage)
                    {
                        // Execute the page capturing the output in the stringwriter.
                        HttpContext.Current.Server.Execute(_holder, sw, false);
                    }
                    else
                    {
                        _control.RenderControl(new HtmlTextWriter(sw));
                    }
                    // Return the output.
                    var html = sw.ToString().RemoveHtmlWhitespace();
                    //extract the form tag
                    parseFormTags(ref html);
                    //reset control
                    removeControlFromHolderPage();
                    return html;
                }
            }
            catch (Exception ex) {
                return string.Format("[Error accurs when rendering {0} :{1}]", typeof(T).FullName, ex.StackTrace);
            }
        }
        /// <summary>
        /// render to a specified page.
        /// </summary>
        /// <param name="page"></param>
        public void Render(IHtmlWriterControl page) {
            if (_verifyRenderingInForm)
            {
                page.HtmlWriter.Write(Render());
            }
            else {
                try
                {
                    // Add it to our holder page.
                    addControlToHolderPage(_verifyRenderingInForm);
                    //render
                    _control.RenderControl(page.HtmlWriter);
                    //prevent polluting the raw page.! Or an exception 'Collection was modified; enumeration operation may not execute' will accur~
                    removeControlFromHolderPage();
                }
                catch (Exception ex) {
                    page.HtmlWriter.Write(Render()); 
                }
            }
        }
        #endregion

		#region helper methods
		private void parseFormTags(ref string html)
		{
			if (_verifyRenderingInForm)
			{
				var match = Regex.Match(html, "<div id=\"tempDiv\">(?<p>.*)</div>");
				var tempData = string.Empty;
				tempData = match.Groups["p"].Value;
				if (tempData.Trim().StartsWith("<div>"))
				{
					match = Regex.Match(tempData, "<div>(?<p>.*)</div>");
					tempData = match.Groups["p"].Value;
				}

				if (!_extractFormTags)
				{
					//do some clear work
					match = Regex.Match(html, "<form.*?>(?<m>.*)</form>");
					tempData = html.Replace(match.Groups["m"].Value, tempData);
				}
				html = tempData;
			}
		}
        void initHolderPage(Page dependingPage)
        {
            //Init the holder page
            if (dependingPage != null)
            {
                _holder = dependingPage;
                _hasDependingPage = true;
            }
            else
            {
                _holder = new Page();
            };
            _holder.EnableViewState = false;
        }
        void addControlToHolderPage(bool verifyRenderingInForm)
        {
            // Add it to our holder page.
            if (verifyRenderingInForm)
            {
                using (var tempWrapper = new HtmlGenericControl("div") { ID = "tempDiv" })
                {
                    tempWrapper.Controls.Add(_control);
                    _formOfControl = new HtmlForm();
                    _formOfControl.ID = String.Format("{0}_Form", _control.GetType().Name);
                    _formOfControl.Controls.Add(tempWrapper);
                    _holder.Controls.Add(_formOfControl);
                }
            }
            else
            {
                _holder.Controls.Add(_control);
            }
        }
        void removeControlFromHolderPage() {
            if (_holder.Controls.Contains(_control)) {
                _holder.Controls.Remove(_control);
            }
        }
		#endregion
	}
}
